
#include <BRender>
#include <BOpenGL>
#include <BReadWrite>

#include "member_BWidget.h"
#include "member_BGlyph.h"
#include "member_BTexture.h"
#include "member_BImage.h"

using namespace BWE;

#define member					(*(member_BGlyph*)_ptr)
#define member_allocate()		_ptr = new member_BGlyph(this)
#define member_release()		delete (member_BGlyph*)_ptr

BGlyph::BGlyph()
{
	member_allocate();
}
BGlyph::BGlyph(const BString& fileName)
{
	member_allocate();
	if (ReadFile(fileName, this))
	{
		image_member(this)->fileName = fileName;
	}
}
BGlyph::BGlyph(int width, int height)
{
	member_allocate();
	setSize(BSize(width, height));
}
BGlyph::BGlyph(const BGlyph& other)
{
	member_allocate();
	setBlend(other.blend());
	setFilter(other.filter());
	setWrap(other.wrap());
	setPolicy(other.policy());
	setFormat(other.format());
	setSize(other.size());
	setPixels(*other.pixels());
}
BGlyph::~BGlyph()
{
	member_release();
}

void BGlyph::copy(const BTexture* other)
{
	BImage::copy(other);
	if (const BGlyph* graphic = dynamic_cast<const BGlyph*>(other))
	{
		member.color = graphic_member(graphic)->color;
		member.view = graphic_member(graphic)->view;
		member.dirty = true;
		member.resized = true;
		setNode(graphic_member(graphic)->node);
	}
}
BImage* BGlyph::clone() const
{
	BGlyph* graphic = new BGlyph();
	graphic->copy(this);
	return graphic;
}

bool BGlyph::setSize(const BSize& size)
{
	if (BImage::setSize(size))
	{
		member.dirty = true;
		member.resized = true;
		return true;
	}
	return false;
}

void BGlyph::setView(const BRect& view)
{
	if (member.view != view)
	{
		member.view = view;
		member.resized = true;
		this->emit(Signal_ViewChanged, view);
	}
}
const BRect& BGlyph::view() const
{
	return member.view;
}

void BGlyph::setNode(BXmlNode* node)
{
	if (member.node != node)
	{
		member.node = node;
		if (member.node)
		{
			const BString& foramtStr = node->attrib("format");
			Format format = member.stringToFormat(foramtStr);
			this->setFormat(format);
			int width = node->attrib("width").toInt();
			int height = node->attrib("height").toInt();
			const BString& viewBoxStr = node->attrib("viewBox");
			if (viewBoxStr.size())
			{
				BStringArray words = viewBoxStr.split(' ');
				member.view.set(words(0).toInt(), words(1).toInt(), words(2).toInt(), words(3).toInt());
				if (width < 1)
					width = member.view.width();
				if (height < 1)
					height = member.view.height();
			}
			else
			{
				member.view.setSize(width, height);
			}
			this->emit(Signal_ViewChanged, member.view);
			this->setSize(width, height);
			for (int i = 0; i < member.node->childCount(); i++)
			{
				BXmlNode* child = member.node->child(i);
				member.refreshStyle(child);
				member.collectDefs(child);
			}
		}
		member.dirty = true;
	}
}
BXmlNode* BGlyph::node()
{
	return member.node;
}

void BGlyph::setColor(BByte r, BByte g, BByte b, BByte a)
{
	setColor(BColor(r, g, b, a));
}
void BGlyph::setColor(const BColor& color)
{
	if (member.color != color)
	{
		member.color = color;
		this->dirty();
	}
}
const BColor& BGlyph::color() const
{
	return member.color;
}

bool BGlyph::capture(BImage* image) const
{
	const BSize& size = this->size();
	if (image == 0 || size.empty())
		return false;
	image->setSize(size);
	Format format = image->format();
	if (format == Format_None)
	{
		format = this->format();
		image->setFormat(format);
	}
	if (BByteArray* pixels = image->pixels())
	{
		if (format == Format_RGBA)
			glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_RGB)
			glReadPixels(0, 0, size.width(), size.height(), GL_RGB, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_Gray_Alpha)
			glReadPixels(0, 0, size.width(), size.height(), GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_Gray)
			glReadPixels(0, 0, size.width(), size.height(), GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_Alpha)
			glReadPixels(0, 0, size.width(), size.height(), GL_ALPHA, GL_UNSIGNED_BYTE, pixels->data());
		image->dirty();
		return true;
	}
	return false;
}
bool BGlyph::capture(BImage* image, const BRect& rect) const
{
	const BSize& size = this->size();
	if (image == 0 || rect.empty() || size.empty())
		return false;
	image->setSize(rect.size());
	Format format = image->format();
	if (format == Format_None)
	{
		format = Format_RGBA;
		image->setFormat(format);
	}
	if (BByteArray* pixels = image->pixels())
	{
		if (format == Format_RGBA)
			glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_RGB && pixels)
			glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), GL_RGB, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_Gray_Alpha && pixels)
			glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_Gray && pixels)
			glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels->data());
		if (format == Format_Alpha && pixels)
			glReadPixels(rect.x(), rect.y(), rect.width(), rect.height(), GL_ALPHA, GL_UNSIGNED_BYTE, pixels->data());
		image->dirty();
		return true;
	}
	return false;
}

void BGlyph::dirty() const
{
	BImage::dirty();
	if (!member.dirty)
	{
		member.dirty = true;
		this->emit(Signal_Dirty);
	}
}
bool BGlyph::flush() const
{
	BTexture::flush();
	GLuint tbo = texture_member(this)->tbo;
	if (tbo == 0)
		return false;
	if (member.dirty)
	{
		member.frame();
		member.check();
		member.dirty = false;
		BImage::flush();
	}
	return true;
}
void BGlyph::paint() const
{
	BPainter painter(this);
	BSizef size = this->size();
	painter.scale(size.width() / member.view.width(), size.height() / member.view.height());
	painter.translate(-member.view.pos());
	for (int i = 0; i < member.node->childCount(); i++)
	{
		BXmlNode* child = member.node->child(i);
		member.paintNode(painter, child);
	}
}
